package com.biz.primus.ms.commodity.service.frontend;

import com.biz.primus.base.enums.vendor.VendorType;
import com.biz.primus.base.exception.utils.AssertUtils;
import com.biz.primus.base.vo.commodity.SaleStatus;
import com.biz.primus.base.vo.promotion.PromotionProductReqVo;
import com.biz.primus.base.vo.promotion.PromotionProductVo;
import com.biz.primus.commodity.exception.CommodityExceptionType;
import com.biz.primus.commodity.vo.*;
import com.biz.primus.commodity.vo.backend.*;
import com.biz.primus.commodity.vo.frontend.ProductRelevantDetail;
import com.biz.primus.common.utils.CopyUtils;
import com.biz.primus.common.utils.JsonUtils;
import com.biz.primus.common.vo.SpringPageVO;
import com.biz.primus.ms.base.vo.SpringPageToSpringPageVO;
import com.biz.primus.ms.commodity.dao.po.*;
import com.biz.primus.ms.commodity.dao.repository.brand.BrandRepository;
import com.biz.primus.ms.commodity.dao.repository.category.CategoryRepository;
import com.biz.primus.ms.commodity.dao.repository.product.CommoditySpecRepository;
import com.biz.primus.ms.commodity.dao.repository.product.GroupProductItemRepository;
import com.biz.primus.ms.commodity.dao.repository.product.GroupProductRepository;
import com.biz.primus.ms.commodity.dao.ro.ProductRo;
import com.biz.primus.ms.commodity.dao.specification.CommoditySpecification;
import com.biz.primus.ms.commodity.dao.specification.GroupProductItemSpecification;
import com.biz.primus.ms.commodity.service.backend.Product2CommodityItemVo;
import com.biz.primus.ms.commodity.trans.GroupProductItem2GroupProductItemItemVo;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author jiangnan
 * @Description: TODO
 * @create 2017-10-13-下午12:14
 */
@Slf4j
@Service
public class CommodityService extends AbstractCommodityService {


    private final GroupProductItemRepository groupProductItemRepository;

    private final GroupProductRepository groupProductRepository;

    private final CommoditySpecRepository commoditySpecRepository;

    private final BrandRepository brandRepository;

    private final CategoryRepository categoryRepository;

    public CommodityService(GroupProductItemRepository groupProductItemRepository, GroupProductRepository groupProductRepository, CommoditySpecRepository commoditySpecRepository, BrandRepository brandRepository, CategoryRepository categoryRepository) {
        this.groupProductItemRepository = groupProductItemRepository;
        this.groupProductRepository = groupProductRepository;
        this.commoditySpecRepository = commoditySpecRepository;
        this.brandRepository = brandRepository;
        this.categoryRepository = categoryRepository;
    }

    public SpringPageVO<CommodityItemVo> findAll(CommodityQueryVo reqVo) {
        Page<Product> page = productRepository.findAll(new CommoditySpecification(reqVo), reqVo.toPageRequest());
        return SpringPageToSpringPageVO.of(page, new Product2CommodityItemVo());
    }

    public PlatformCommodityDetailVo getCommodityDetailVo(Long productId) {
        Product product = productRepository.findOne(productId);
        AssertUtils.notNull(product, CommodityExceptionType.PRODUCT_NOT_FOUND);
        return this.getPlatformCommodityDetailVo(product);
    }

    public void syncCommodity(SyncCommodityVo reqVo) {
        try {
            Product product = productRepository.findByProductCode(reqVo.getProductCode());
            if (product == null) {
                product = new Product();
                product.setId(idService.getNextId());
            }
            this.updateCommodity(reqVo, product);
            productRepository.save(product);
        } catch (Exception e) {
            log.warn("syncCommoditySpec warn reqVo {}", reqVo);
            e.printStackTrace();
        }

    }

    public void syncCommodities(List<SyncCommodityVo> reqVos) {
        log.info("erp syncCommodities {}" + JsonUtils.obj2Json(reqVos));
        reqVos.forEach(this::syncCommodity);
        log.info(("erp syncCommodities complete"));
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void syncCommoditySpec(SyncCommoditySpecVo reqVo) {
        try {
            CommoditySpec commoditySpec = commoditySpecRepository.findByProductCodeAndPackageId(reqVo.getProductCode(), reqVo.getPackageId());
            if (commoditySpec == null) {
                commoditySpec = new CommoditySpec();
                commoditySpec.setId(idService.getNextId());
            }
            CopyUtils.copyProperties(reqVo, commoditySpec);
            commoditySpecRepository.save(commoditySpec);
            if (commoditySpec.getTruePackage() != null && commoditySpec.getTruePackage() && commoditySpec.getPackageNumber() > 1) {
                Product product = productRepository.findByProductCode(reqVo.getProductCode());
                if (product == null) {
                    log.warn("productNotFound  productCode {}", reqVo.getProductCode());
                    return;
                }
                product.setPackageNumber(reqVo.getPackageNumber());
                this.saveProduct(product);
            }

        } catch (Exception e) {
            log.warn("syncCommoditySpec warn reqVo {}", reqVo);
            e.printStackTrace();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void delCommoditySpec(SyncCommoditySpecVo reqVo) {
        CommoditySpec commoditySpec = commoditySpecRepository.findByProductCodeAndPackageId(reqVo.getProductCode(), reqVo.getPackageId());
        if (commoditySpec.getTruePackage()) {
            Product product = productRepository.findByProductCode(reqVo.getProductCode());
            if (product == null) {
                log.warn("productNotFound  productCode {}", reqVo.getProductCode());
                return;
            }
            product.setPackageNumber(0);
            this.saveProduct(product);
        }
        commoditySpecRepository.delete(commoditySpec.getId());
    }

    public void syncCommoditySepcs(List<SyncCommoditySpecVo> reqVos) {
        log.info("erp syncCommoditySepcs {}" + JsonUtils.obj2Json(reqVos));
        reqVos.forEach(this::syncCommoditySpec);
        log.info(("erp syncCommoditySpec complete"));
    }


    @Transactional(rollbackFor = Exception.class)
    public void save(CommodityUpdateVo reqVo, SyncCommodityVo baseVo) {
        Product product = productRepository.findOne(reqVo.getProductId());
        if (Objects.equals(product.getIsRapidProduct(), Boolean.TRUE)) {
            this.updateCommodity(baseVo, product);
        }
        AssertUtils.notNull(product, CommodityExceptionType.PRODUCT_NOT_FOUND);
        this.platformUpdateCommodity(product, reqVo);
        this.saveProduct(product);
    }


    @Transactional(rollbackFor = Exception.class)
    public void updateSaleStatus(UpdateSaleStatusVo reqVo) {
        Product product = productRepository.findOne(reqVo.getProductId());
        AssertUtils.notNull(product, CommodityExceptionType.PRODUCT_NOT_FOUND);
        product.setSaleStatus(reqVo.getSaleStatus());
        this.saveProduct(product);
    }

    @Transactional(rollbackFor = Exception.class)
    public void batchSetPoints(Integer points) {
        productRepository.updateAllProductPoints(points);
    }


    public SpringPageVO<GroupProductItemItemVo> groupProductItemFindAll(GroupProductItemQueryVo queryVo) {
        Page<GroupProductItem> all = groupProductItemRepository.findAll(new GroupProductItemSpecification(queryVo), queryVo.toPageRequest());
        return SpringPageToSpringPageVO.of(all, new GroupProductItem2GroupProductItemItemVo());
    }

    public GroupProductDetailVo getGroupProductDetailVo(Long groupProductId) {
        log.info("getGroupProductDetailVo {}", groupProductId);
        GroupProduct one = groupProductRepository.findOne(groupProductId);
        AssertUtils.notNull(one, CommodityExceptionType.PRODUCT_NOT_FOUND, "组合商品不存在");
        GroupProductDetailVo respVo = new GroupProductDetailVo();
        respVo.setId(one.getId());
        respVo.setProductCode(one.getProduct().getProductCode());
        List<GroupProductItem> itemList = one.getItemList();
        respVo.setGroupProductItemVos(Lists.transform(itemList, new GroupProductItem2GroupProductItemItemVo()));
        return respVo;
    }

    @Transactional(rollbackFor = Exception.class)
    public void createGroupProduct(GroupProductDetailVo reqVo) {
        log.info("createGroupProduct {}", reqVo);
        AssertUtils.isTrue(StringUtils.isNotBlank(reqVo.getProductCode()) && reqVo.getProductCode().startsWith("ZH"),
                CommodityExceptionType.COMMON_EXCEPTION, "商品编码不合法");
        Product valid = productRepository.findByProductCode(reqVo.getProductCode());
        AssertUtils.isNull(valid, CommodityExceptionType.COMMON_EXCEPTION, "该组合商品编码已经存在");
        Product product = new Product();
        product.setId(idService.getNextId());
        product.setProductCode(reqVo.getProductCode());
        GroupProduct groupProduct = new GroupProduct();
        groupProduct.setId(idService.getNextId());
        groupProduct.setProduct(product);
        List<GroupProductItem> groupProductItems = getGroupProductItems(groupProduct, reqVo.getGroupProductItemVos());
        groupProduct.setItemList(groupProductItems);
        product.setGroupProduct(groupProduct);
        productRepository.save(product);
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateGroupProduct(GroupProductDetailVo reqVo) {
        log.info("createGroupProduct {}", reqVo);
        GroupProduct groupProduct = groupProductRepository.findOne(reqVo.getId());
        AssertUtils.notNull(groupProduct, CommodityExceptionType.PRODUCT_NOT_FOUND, "组合商品不存在");
        List<GroupProductItemItemVo> groupProductItemVos = reqVo.getGroupProductItemVos();
        AssertUtils.isTrue(CollectionUtils.isNotEmpty(groupProductItemVos), CommodityExceptionType.COMMON_EXCEPTION,
                "组合商品必须有子商品");
        groupProduct.getItemList().clear();
        groupProductRepository.save(groupProduct);
        List<GroupProductItem> groupProductItems = getGroupProductItems(groupProduct, groupProductItemVos);
        groupProductItemRepository.save(groupProductItems);
    }

    private List<GroupProductItem> getGroupProductItems(GroupProduct groupProduct, List<GroupProductItemItemVo> groupProductItemVos) {
        List<GroupProductItem> groupProductItems = Lists.newArrayList();
        for (GroupProductItemItemVo index : groupProductItemVos) {
            GroupProductItem groupProductItem = new GroupProductItem();
            groupProductItem.setParent(groupProduct);
            groupProductItem.setPriceFactor(index.getPriceFactor());
            Product product = productRepository.findByProductCode(index.getProductCode());
            AssertUtils.notNull(product, CommodityExceptionType.PRODUCT_NOT_FOUND, "组合商品子商品不存在");
            groupProductItem.setProduct(product);
            groupProductItem.setQuantity(index.getQuantity());
            groupProductItem.setId(idService.getNextId());
            groupProductItems.add(groupProductItem);
        }
        return groupProductItems;
    }

    public SpringPageVO<PromotionProductVo> findAll(PromotionProductReqVo reqVo) {
        CommodityQueryVo commodityQueryVo = new CommodityQueryVo();
        commodityQueryVo.setCategoryId(reqVo.getCategoryId());
        commodityQueryVo.setBrandId(reqVo.getBrandId());
        commodityQueryVo.setProductName(reqVo.getKeyword());
        Page<Product> page = productRepository.findAll(new CommoditySpecification(commodityQueryVo), reqVo.toPageRequest());
        List<PromotionProductVo> respVo = Lists.newArrayList();
        for (Product index : page.getContent()) {
            PromotionProductVo promotionProductVo = new PromotionProductVo();
            promotionProductVo.setProductId(index.getId());
            promotionProductVo.setProductName(index.getName());
            promotionProductVo.setProductCode(index.getProductCode());
            promotionProductVo.setCategoryId(index.getCategoryId());
            promotionProductVo.setBrandId(index.getBrandId());
            promotionProductVo.setBrandName(Optional.ofNullable(brandRepository.findOne(index.getBrandId())).orElse(new Brand()).getName());
            promotionProductVo.setCategoryName(Optional.ofNullable(categoryRepository.findOne(index.getCategoryId())).orElse(new Category()).getName());
        }
        return new SpringPageVO.Builder<PromotionProductVo>().content(respVo).number(page.getNumber()).size(page.getSize()).totalElements(page.getTotalElements()).build();
    }

    /**
     * 根据条形码 获取商品Id
     *
     * @param barCode
     * @return
     */
    public Long findProductIdByBarCode(String barCode) {
        log.debug("findProductIdByBarCode :{}", barCode);
        CommoditySpec commoditySpec = commoditySpecRepository.findByBarCode(barCode);
        if (commoditySpec != null && StringUtils.isNotBlank(commoditySpec.getProductCode())) {
            Product product = productRepository.findByProductCode(commoditySpec.getProductCode());
            if (product != null) {
                return product.getId();
            }
        }
        return null;
    }

    /**
     * 获得商品规格
     *
     * @param productId
     * @return todo:用redis做一级缓存
     */
    public List<ProductRelevantDetail> getRelevant(Long productId) {
        log.debug("getRelevant productId:{}", productId);
        Product product = productRepository.findOne(productId);
        if (product == null) {
            log.debug("product not exist");
            return null;
        }
        RelevanceProduct relevanceProduct = product.getRelevanceProduct();
        if (relevanceProduct == null || CollectionUtils.isEmpty(relevanceProduct.getProducts())) {
            log.debug("relevanceProduct not exist");
            return null;
        }
        List<ProductRelevantDetail> resp = relevanceProduct.getProducts().stream().filter(p -> !Objects.equals(p.getSaleStatus(), SaleStatus.ON_SALE)).map((p) -> {
            return new ProductRelevantDetail(p.getName(), p.getId(), p.getProductCode());
        }).collect(Collectors.toList());
        return resp;
    }

    public List<String> getTypeProductCodes(VendorType vendorType) {
        List<String> typeProductCodes = productRedisDao.getTypeProductCodes(vendorType);
        if (typeProductCodes == null) {
            List<String> productCodes = productRepository.findProductCodes(vendorType, SaleStatus.ON_SALE);
            productRedisDao.updateProductCodes(vendorType, productCodes);
            return productCodes;
        }
        return typeProductCodes;
    }

    public CommodityVo findCommodityByCode(String productCode) {
        AssertUtils.notNull(productCode, CommodityExceptionType.COMMON_EXCEPTION, "商品编码为空");
        ProductRo productRo = getProductRo(productCode);
        return CopyUtils.copyProperties(productRo, new CommodityVo());
    }

    public List<CommodityVo> findCommodityByCodes(List<String> productCodes) {
        List<ProductRo> productRos = productRedisDao.findByIdsWithNull(productCodes);
        List<CommodityVo> respVos = Lists.newArrayList();
        productRos.forEach((p)->respVos.add(p==null?null:CopyUtils.copyProperties(p,new CommodityVo())));
        return respVos;
    }

    public List<CommodityVo> findByCodesFromDb(List<String> codes){
        List<Product> products= productRepository.findByProductCodeIn(codes);
        List<CommodityVo> respVos = Lists.newArrayList();
        products.forEach((p)->respVos.add(p==null?null:CopyUtils.copyProperties(p,new CommodityVo())));
        return respVos;
    }
}
